home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
AmigActive 23
/
AACD 23.iso
/
AACD
/
Online
/
opennap
/
add_file.c
< prev
next >
Wrap
C/C++ Source or Header
|
2001-06-08
|
14KB
|
619 lines
/* Copyright (C) 2000-1 drscholl@users.sourceforge.net
This is free software distributed under the terms of the
GNU Public License.
$Id: add_file.c,v 1.93 2001/02/23 23:38:48 drscholl Exp $ */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include "opennap.h"
#include "debug.h"
#ifndef ROUTING_ONLY
/* allowed bitrates for MPEG V1/V2 Layer III */
const int BitRate[18] =
{ 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 192, 224,
256, 320
};
/* allowed sample rates for MPEG V2/3 */
const int SampleRate[6] = { 16000, 24000, 22050, 32000, 44100, 48000 };
#if DEBUG
int
validate_flist (FLIST * p)
{
ASSERT_RETURN_IF_FAIL (VALID_LEN (p, sizeof (FLIST)), 0);
ASSERT_RETURN_IF_FAIL (p->magic == MAGIC_FLIST, 0);
ASSERT_RETURN_IF_FAIL (VALID_LEN (p->key, strlen (p->key) + 1), 0);
ASSERT_RETURN_IF_FAIL (p->count == list_count (p->list), 0);
return 1;
}
#endif
static void
fdb_add (HASH * table, char *key, DATUM * d, int track_key)
{
FLIST *files;
LIST *list;
ASSERT (table != 0);
ASSERT (key != 0);
ASSERT (d != 0);
files = hash_lookup (table, key);
/* if there is no entry for this particular word, create one now */
if (!files)
{
files = CALLOC (1, sizeof (FLIST));
if (!files)
{
OUTOFMEMORY ("fdb_add");
return;
}
#if DEBUG
files->magic = MAGIC_FLIST;
#endif
files->key = STRDUP (key);
if (!files->key)
{
OUTOFMEMORY ("fdb_add");
FREE (files);
return;
}
if (hash_add (table, files->key, files))
{
FREE (files->key);
FREE (files);
return;
}
}
ASSERT (validate_flist (files));
list = CALLOC (1, sizeof (LIST));
if (!list)
{
OUTOFMEMORY ("list");
if (!files->list)
hash_remove (table, files->key);
return;
}
list->data = d;
/* order is not important, push to beginning of list for speed */
files->list = list_push (files->list, list);
files->count++;
/* this is conditional since we don't keep track of the key when
* inserting the hash value for the RESUME function.
*/
if (track_key)
{
/* keep a list of search tokens that match this file */
list = CALLOC (1, sizeof (LIST));
if (!list)
{
/* this is bad. we've already committed a lot of things, just
* bail out as best as possible.
*/
OUTOFMEMORY ("fdb_add");
return;
}
list->data = files;
d->tokens = list_push (d->tokens, list);
}
}
/* common code for inserting a file into the various hash tables */
static void
insert_datum (DATUM * info, char *av)
{
LIST *tokens, *ptr;
unsigned int fsize;
ASSERT (info != 0);
ASSERT (av != 0);
if (!info->user->con->uopt->files)
{
/* create the hash table */
info->user->con->uopt->files =
hash_init (257, (hash_destroy) free_datum);
if (!info->user->con->uopt->files)
{
OUTOFMEMORY ("insert_datum");
return;
}
}
hash_add (info->user->con->uopt->files, info->filename, info);
/* split the filename into words */
tokens = tokenize (av, NULL);
/* the filename may not consist of any searchable words, in which
* case its not entered into the index. this file will only be seen
* when browsing the user possessing it
*/
for (ptr = tokens; ptr; ptr = ptr->next)
fdb_add (File_Table, ptr->data, info, 1);
list_free (tokens, 0);
#if RESUME
/* index by md5 hash */
fdb_add (MD5, info->hash, info, 0);
#endif
fsize = info->size / 1024; /* in kbytes */
info->user->shared++;
info->user->libsize += fsize;
Num_Gigs += fsize; /* this is actually kB, not gB */
Num_Files++;
Local_Files++;
info->user->sharing = 1; /* note that we began sharing */
}
static DATUM *
new_datum (char *filename, char *hash)
{
DATUM *info = CALLOC (1, sizeof (DATUM));
(void) hash;
if (!info)
{
OUTOFMEMORY ("new_datum");
return 0;
}
info->filename = STRDUP (filename);
if (!info->filename)
{
OUTOFMEMORY ("new_datum");
FREE (info);
return 0;
}
#if RESUME
info->hash = STRDUP (hash);
if (!info->hash)
{
OUTOFMEMORY ("new_datum");
FREE (info->filename);
FREE (info);
return 0;
}
#endif
return info;
}
static int
bitrateToMask (int bitrate, USER * user)
{
unsigned int i;
for (i = 0; i < sizeof (BitRate) / sizeof (int); i++)
{
if (bitrate <= BitRate[i])
return i;
}
log ("bitrateToMask(): invalid bit rate %d (%s, \"%s\")", bitrate,
user->nick, user->clientinfo);
return 0; /* invalid bitrate */
}
static int
freqToMask (int freq, USER * user)
{
unsigned int i;
for (i = 0; i < sizeof (SampleRate) / sizeof (int); i++)
{
if (freq <= SampleRate[i])
return i;
}
log ("freqToMask(): invalid sample rate %d (%s, \"%s\")", freq,
user->nick, user->clientinfo);
return 0;
}
/* 100 "<filename>" <md5sum> <size> <bitrate> <frequency> <time>
client adding file to the shared database */
HANDLER (add_file)
{
char *av[6];
DATUM *info;
unsigned int fsize;
(void) tag;
(void) len;
ASSERT (validate_connection (con));
CHECK_USER_CLASS ("add_file");
ASSERT (validate_user (con->user));
if (!option(ON_ALLOW_SHARE))
return;
if (Max_Shared && con->user->shared > Max_Shared)
{
send_cmd (con, MSG_SERVER_NOSUCH,
"You may only share %d files", Max_Shared);
return;
}
if (split_line (av, sizeof (av) / sizeof (char *), pkt) != 6)
{
unparsable (con);
return;
}
if (is_blocked (av[0]))
return;
if (av[1] - av[0] > _POSIX_PATH_MAX + 2)
{
send_cmd (con, MSG_SERVER_NOSUCH, "filename too long");
return;
}
/* ensure we have a valid byte count */
fsize = strtoul (av[2], 0, 10);
/* check for overflow */
if (con->user->libsize + fsize < con->user->libsize)
{
log ("add_file(): %u byte file would overflow %s's library size",
fsize, con->user->nick);
return;
}
/* make sure this isn't a duplicate - only compare the basename, not
* including the directory component
*/
if (con->uopt->files && hash_lookup (con->uopt->files, av[0]))
{
send_cmd (con, MSG_SERVER_NOSUCH, "duplicate file");
return;
}
/* create the db record for this file */
if (!(info = new_datum (av[0], av[1])))
return;
info->user = con->user;
info->size = fsize;
info->bitrate = bitrateToMask (atoi (av[3]), con->user);
info->frequency = freqToMask (atoi (av[4]), con->user);
info->duration = atoi (av[5]);
info->type = CT_MP3;
insert_datum (info, av[0]);
}
char *Content_Types[] = {
"mp3", /* not a real type, but what we use for audio/mp3 */
"audio",
"video",
"application",
"image",
"text"
};
/* 10300 "<filename>" <size> <hash> <content-type> */
HANDLER (share_file)
{
char *av[4];
DATUM *info;
int i, type;
unsigned int fsize;
(void) len;
(void) tag;
ASSERT (validate_connection (con));
CHECK_USER_CLASS ("share_file");
if (!option(ON_ALLOW_SHARE))
return;
if (Max_Shared && con->user->shared > Max_Shared)
{
log ("add_file(): %s is already sharing %d files", con->user->nick,
con->user->shared);
if (ISUSER (con))
send_cmd (con, MSG_SERVER_NOSUCH,
"You may only share %d files", Max_Shared);
return;
}
if (split_line (av, sizeof (av) / sizeof (char *), pkt) != 4)
{
unparsable (con);
return;
}
if (is_blocked (av[0]))
return;
/* make sure the content-type looks correct */
type = -1;
for (i = CT_AUDIO; i < CT_UNKNOWN; i++)
{
if (!strcasecmp (Content_Types[i], av[3]))
{
type = i;
break;
}
}
if (type == -1)
{
log ("share_file(): not a valid type: %s", av[3]);
if (ISUSER (con) == CLASS_USER)
send_cmd (con, MSG_SERVER_NOSUCH, "%s is not a valid type",
av[3]);
return;
}
if (av[1] - av[0] > _POSIX_PATH_MAX + 2)
{
send_cmd (con, MSG_SERVER_NOSUCH, "filename too long");
return;
}
if (con->uopt->files && hash_lookup (con->uopt->files, av[0]))
{
send_cmd (con, MSG_SERVER_NOSUCH, "duplicate file");
return;
}
fsize = strtoul (av[1], 0, 10);
if (fsize + con->user->libsize < con->user->libsize)
{
log ("share_file(): %u byte file would overflow %s's library size",
fsize, con->user->nick);
return;
}
if (!(info = new_datum (av[0], av[2])))
return;
info->user = con->user;
info->size = fsize;
info->type = type;
insert_datum (info, av[0]);
}
/* 870 "<directory>" "<basename>" <md5> <size> <bitrate> <freq> <duration> [ ... ]
client command to add multiple files in the same directory */
HANDLER (add_directory)
{
char *dir, *basename, *md5, *size, *bitrate, *freq, *duration;
char path[_POSIX_PATH_MAX], dirbuf[_POSIX_PATH_MAX];
int pathlen, fsize;
DATUM *info;
(void) tag;
(void) len;
ASSERT (validate_connection (con));
CHECK_USER_CLASS ("add_directory");
if (!option(ON_ALLOW_SHARE))
return;
dir = next_arg (&pkt); /* directory */
if (!dir)
{
log ("add_directory(): missing directory component");
return;
}
pathlen = strlen (dir);
if ((size_t) pathlen >= sizeof (dirbuf) - 1)
{
log ("add_directory(): directory component is too long, ignoring");
return;
}
ASSERT ((size_t) pathlen < sizeof (dirbuf) - 1);
dirbuf[sizeof (dirbuf) - 1] = 0; /* ensure nul termination */
strncpy (dirbuf, dir, sizeof (dirbuf) - 1);
if (pathlen > 0 && dirbuf[pathlen - 1] != '\\')
{
dirbuf[pathlen++] = '\\';
dirbuf[pathlen] = 0;
if ((size_t) pathlen >= sizeof (dirbuf) - 1)
{
ASSERT ((size_t) pathlen < sizeof (dirbuf));
log
("add_directory(): directory component is too long, ignoring");
return;
}
}
/* if the client passes a dir + file that is longer than 255 chars,
* strncpy() won't write a \0 at the end of the string, so ensure that
* this always happens
*/
path[sizeof (path) - 1] = 0;
while (pkt)
{
if (Max_Shared && con->user->shared > Max_Shared)
{
send_cmd (con, MSG_SERVER_NOSUCH,
"You may only share %d files", Max_Shared);
return;
}
basename = next_arg (&pkt);
md5 = next_arg (&pkt);
size = next_arg (&pkt);
bitrate = next_arg (&pkt);
freq = next_arg (&pkt);
duration = next_arg (&pkt);
if (!basename || !md5 || !size || !bitrate || !freq || !duration)
{
unparsable (con);
return;
}
strncpy (path, dirbuf, sizeof (path) - 1);
strncpy (path + pathlen, basename, sizeof (path) - 1 - pathlen);
ASSERT (path[sizeof (path) - 1] == 0);
/* TODO: still seeing crashes here, we must be overwriting the
* stack on occasion. quit now if we detect this condition
*/
if (path[sizeof (path) - 1] != 0)
{
log ("add_directory: ERROR! buffer overflow detected");
return;
}
if (is_blocked (path))
continue;
if (con->uopt->files && hash_lookup (con->uopt->files, path))
{
send_cmd (con, MSG_SERVER_NOSUCH, "Duplicate file");
continue; /* get next file */
}
fsize = atoi (size);
if (fsize < 1)
{
send_cmd (con, MSG_SERVER_NOSUCH, "invalid size");
continue;
}
/* create the db record for this file */
if (!(info = new_datum (path, md5)))
return;
info->user = con->user;
info->size = fsize;
info->bitrate = bitrateToMask (atoi (bitrate), con->user);
info->frequency = freqToMask (atoi (freq), con->user);
info->duration = atoi (duration);
info->type = CT_MP3;
insert_datum (info, path);
}
}
#endif /* ! ROUTING_ONLY */
/* 10012 <nick> <shared> <size>
remote server is notifying us that one of its users is sharing files */
HANDLER (user_sharing)
{
char *av[3];
USER *user;
int n;
double libsize;
int err = 0;
(void) len;
ASSERT (validate_connection (con));
CHECK_SERVER_CLASS ("user_sharing");
if (split_line (av, sizeof (av) / sizeof (char *), pkt) != 3)
{
log ("user_sharing: wrong number of arguments");
return;
}
user = hash_lookup (Users, av[0]);
if (!user)
{
log ("user_sharing: no such user %s (from %s)", av[0], con->host);
return;
}
do
{
n = atoi (av[1]);
if (n < 0)
{
log ("user_sharing: negative file count for user %s", user->nick);
err = 1;
break;
}
ASSERT (Num_Files >= user->shared);
Num_Files -= user->shared;
Num_Files += n;
user->shared = n;
libsize = strtoul (av[2], 0, 10);
if (libsize < 0)
{
log ("user_sharing: negative library size for user %s",
user->nick);
err = 1;
break;
}
if (Num_Gigs < user->libsize)
{
log
("user_sharing: error: Num_Gigs=%f, user->libsize=%u user->nick=%s",
Num_Gigs, user->libsize, user->nick);
Num_Gigs = user->libsize; /* prevent negative count */
err = 1;
break;
}
Num_Gigs -= user->libsize;
Num_Gigs += libsize;
user->libsize = libsize;
}
while (0);
if (err)
{
/* reset counts */
Num_Files -= user->shared;
Num_Gigs -= user->libsize;
user->shared = 0;
user->libsize = 0;
}
pass_message_args (con, tag, "%s %hu %u", user->nick, user->shared,
user->libsize);
}
/* 110 [:sender]
* unshare all files
*/
HANDLER (unshare_all)
{
USER *sender;
char *sender_name;
(void) len;
if (pop_user_server (con, tag, &pkt, &sender_name, &sender))
return;
ASSERT (sender != 0);
#ifndef ROUTING_ONLY
if (ISUSER (con))
{
if (!con->uopt->files)
{
ASSERT (sender->shared == 0);
return; /* nothing shared */
}
send_cmd (con, tag, "%d", sender->shared);
free_hash (con->uopt->files);
con->uopt->files = 0;
ASSERT (Local_Files >= sender->shared);
Local_Files -= sender->shared;
}
#endif /* !ROUTING_ONLY */
if (sender->libsize > Num_Gigs)
{
log ("unshare_all: sender->libsize=%u Num_Gigs=%f",
sender->libsize, Num_Gigs);
Num_Gigs = sender->libsize;
}
ASSERT (Num_Gigs >= sender->libsize);
Num_Gigs -= sender->libsize;
ASSERT (Num_Files >= sender->shared);
Num_Files -= sender->shared;
sender->libsize = 0;
sender->shared = 0;
pass_message_args (con, tag, ":%s", sender->nick);
}